<template>
  <div class="temp-page">
    <div id="container"></div>
  </div>
  <Transition name="slide-fade">
    <div class="inner-dialog" v-if="state.dialogInnerVisible">
      <header class="inner-dialog-title">图谱详情</header>
      <el-tabs v-model="state.activeName" class="demo-tabs" @tab-click="handleClick">
        <el-tab-pane label="图谱详细内容" name="1">
          <div class="info-list"><span class="info-list-title">案件名称: </span><span>{{
            state.innerDialogInfo.name }}</span></div>
          <el-divider style="margin:10px 0" />
          <div class="info-list"><span class="info-list-title">案件类型: </span><span>{{
            state.innerDialogInfo.type }}</span></div>
          <el-divider style="margin:10px 0" />
          <div class="info-list"><span class="info-list-title">业务领域: </span><span>{{
            state.innerDialogInfo['业务领域'] }}</span></div>
        </el-tab-pane>
        <el-tab-pane label="图谱拓展说明" name="2"></el-tab-pane>
      </el-tabs>
    </div>
  </Transition>
</template>

<script setup name="Atlas">
import G6 from '@antv/g6';
import insertCss from 'insert-css';
import * as _ from 'lodash'
import { ElMessage } from "element-plus";
import { chatData } from "./data"
import { chatData2 } from "./data2"

const emit = defineEmits([''])
const props = defineProps({
  layout: {
    //布局模式
    type: String,
    default: 'fruchterman'//fruchterman  force2
  },
  isToolbar: { //是否显示提示框
    type: Boolean,
    default: true
  },
  isLegend: { //是否显示图例
    type: Boolean,
    default: true
  },
  isDetails: {//点击节点是否显示详情弹窗
    type: Boolean,
    default: true
  },
})
insertCss(`
.g6-tooltip {
  border: 1px solid #e2e2e2;
  border-radius: 4px;
  font-size: 12px;
  color: #545454;
  background-color: rgba(255, 255, 255, 0.9);
  padding: 10px 8px;
  box-shadow: rgb(174, 174, 174) 0px 0px 10px;
}
#contextMenu {
  position: absolute;
  list-style-type: none;
  padding: 10px 8px;
  left: -150px;
  background-color: rgba(255, 255, 255, 0.9);
  border: 1px solid #e2e2e2;
  border-radius: 4px;
  font-size: 12px;
  color: #545454;
}
`);
const state = reactive({
  dialogInnerVisible: false,
  id: '',
  mappingData: { edges: [], nodes: [] },
  rootData: { id: "" },
  changeInitKey: 0,
  typeArray: [],
  listDate: {},
  innerDialogInfo: {},
  activeName: "1"
});
const colorArr = [
  'rgb(128,177,254)',
  'rgb(254,133,68)',
  'rgb(207,149,244)',
  'rgb(254,144,196)',
  '#1D62A9',
  '#45CF3B',
  '#3BCF96',
  '#ED4747',
  '#EDB047',
  '#47B0ED',
  '#ED47C9',
  '#B340FF',
  '#F0427F',
  '#FF7F42',
  '#FF4242',
  '#FFD700',
  '#FFA500',
  '#FF6347',
  '#FF4500',
]
const handleClick = () => {

}
let graph = {}
//工具栏
const toolbar = new G6.ToolBar({
  position: { x: 50, y: 50 },
});
//右键菜单
const contextMenu = new G6.Menu({
  getContent(evt) {
    //let element = `<h3>${evt.item._cfg.model.type}</h3><div id="lockNode">${evt.item._cfg.model.isLock ? '解锁' : '锁定'}节点</div>`;
    let element = `<h3>${evt.item._cfg.model.type}</h3>`;

    if (state.rootData.id === evt.item._cfg.model.id) {
      element = `<h3>${evt.item._cfg.model.type}</h3>`;
    }
    return element
  },
  handleMenuClick: (target, val) => {
    if (target.id === "lockNode") {
      state.mappingData.nodes.forEach(item => {
        if (item.id === val._cfg.model.id) {
          if (item.isLock) {
            item.isLock = false
            graph.updateItem(val, {
              icon: {
                show: false,
              },
            });
            ElMessage({ message: '已解锁节点', type: 'success' });
          } else {
            item.isLock = true
            graph.updateItem(val, {
              icon: {
                show: true,
                img: '/src/assets/images/lock.png',
                width: 20,
                height: 20
              },
            });
            ElMessage({ message: '已锁定节点', type: 'success' });
          }
        }
      })

    }
  },
  // 需要加上父级容器的 padding-left 16 与自身偏移量 10
  offsetX: 16 + 10,
  // 需要加上父级容器的 padding-top 24 、画布兄弟元素高度、与自身偏移量 10
  offsetY: 0,
  // 在哪些类型的元素上响应'edge', 'canvas'
  itemTypes: ['node'],
});
//提示框
const tooltip = new G6.Tooltip({
  offsetX: 10,
  offsetY: 10,
  fixToNode: [1, 0.5],
  // 允许出现 tooltip 的 item 类型
  itemTypes: ['node'],
  // 自定义 tooltip 内容
  getContent: (e) => {
    const outDiv = document.createElement('div');
    outDiv.style.width = 'fit-content';
    outDiv.style.height = 'fit-content';
    const model = e.item.getModel();
    outDiv.innerHTML = `${model.name}`;
    return outDiv;
  },
});

const initChart = () => {
  //图例
  const legendData = { nodes: [] }
  state.typeArray.forEach((item, index) => {
    legendData.nodes.push({
      id: item.label,
      label: item.label,
      style: {
        fill: item.color,
      }
    })
  })
  const legend = new G6.Legend({
    data: legendData,
    align: 'center',
    layout: 'horizontal', // vertical  行或列
    position: 'bottom-left',
    horiSep: 24,
    //offsetY: 24,
    padding: [14, 16, 8, 16],
    containerStyle: {
      fill: '#fff',
      lineWidth: 0
    },
  });
  //修改节点样式
  G6.registerNode('案件信息', {
    afterDraw(cfg, group) {
      const r = cfg.size / 2;
      const back1 = group.addShape('circle', {
        zIndex: -3,
        attrs: {
          x: 0,
          y: 0,
          r,
          fill: cfg.color,
          opacity: 0.6,
        },
        name: 'back1-shape',
      });
      const back2 = group.addShape('circle', {
        zIndex: -2,
        attrs: {
          x: 0,
          y: 0,
          r,
          fill: cfg.color,
          opacity: 0.6,
        },
        name: 'back2-shape',
      });
      const back3 = group.addShape('circle', {
        zIndex: -1,
        attrs: {
          x: 0,
          y: 0,
          r,
          fill: cfg.color,
          opacity: 0.6,
        },
        name: 'back3-shape',
      });
      group.sort();
      back1.animate(
        {
          r: r + 10,
          opacity: 0.1,
        },
        {
          duration: 3000,
          easing: 'easeCubic',
          delay: 0,
          repeat: true, // repeat
        },
      ); // no delay
      back2.animate(
        {
          // Magnifying and disappearing
          r: r + 10,
          opacity: 0.1,
        },
        {
          duration: 3000,
          easing: 'easeCubic',
          delay: 1000,
          repeat: true, // repeat
        },
      ); // 1s delay
      back3.animate(
        {
          // Magnifying and disappearing
          r: r + 10,
          opacity: 0.1,
        },
        {
          duration: 3000,
          easing: 'easeCubic',
          delay: 2000,
          repeat: true, // repeat
        },
      ); // 3s delay
    },
  },
    'circle',
  );
  const container = document.getElementById('container');
  const width = container.scrollWidth;
  const height = container.scrollHeight || 500;
  let plugins = [tooltip, contextMenu]
  if (props.isToolbar) {
    plugins.push(toolbar)
  }
  if (props.isLegend) {
    plugins.push(legend)
  }
  graph = new G6.Graph({
    container: 'container',
    width,
    height,
    plugins,
    modes: {
      default: ['drag-canvas', 'zoom-canvas', 'drag-node'],//'activate-relations' // 允许拖拽画布、放缩画布、拖拽节点
    },
    defaultNode: {
      type: 'bubble',
      size: 34,
      collapsed: true,
      //color: "rgb(254,133,68)",
      style: {
        //fill: 'rgb(128,177,254)',
      },
      labelCfg: {
        position: 'center',
        style: {
          fill: '#909399',
          //fontStyle: 'bold',
        },
      },
    },
    defaultEdge: {
      //color: 'red',
      //type: 'animate-line',
      style: {
        endArrow: true,
      },
      labelCfg: {
        autoRotate: true,
        //refY: -10,
        style: {
          fill: '#bbbbbd',
          stroke: 'white',
          lineWidth: 10,
        }
      },
    },
    animate: true,
    layout: { //布局的方法及其配置项
      gravity: 1, //控制边的距离
      speed: 20,
      tick: () => {
        graph.refreshPositions()
      },
      type: props.layout,         // 指定为力导向布局 force
      preventOverlap: true,  // 防止节点重叠
      nodeSize: 200,       // 节点大小，用于算法中防止节点重叠时的碰撞检测。由于已经在上一节的元素配置中
      linkDistance: 100,     // 指定边距离为100设置了每个节点的 size 属性，则不需要在此设置 nodeSize。
      //center: [600, 250],
      factor: 8,
    }
  });

  graph.node(function (node) {
    let color = ""
    state.typeArray.forEach((item) => {
      if (item.label === node.type) {
        color = item.color
      }
    })
    let iconInfo = {
      show: true,
      img: 'https://gw.alipayobjects.com/zos/basement_prod/012bcf4f-423b-4922-8c24-32a89f8c41ce.svg',
      width: 20,
      height: 20
    }
    //单独处理节点样式
    return {
      label: node.name ? node.name.slice(0, 8) : "",
      color: color,
      style: {
        fill: color,
      },
      icon: node.id == state.rootData.id ? iconInfo : { show: node.showImg, img: node.img },
      labelCfg: {
        position: 'bottom',
        offset: 12,
        style: {
          textAlign: 'center',
        },
      },
    };
  });
  graph.data(state.mappingData);
  graph.render(); // 渲染图
  let timer = null;
  graph.on('node:dblclick', function (evt) {
    clearTimeout(timer); //清除未执行的定时器

    if (evt.item._cfg.model.isLock) {
      ElMessage({ message: '节点已锁定', type: 'error' });
      return
    }
    let arr = _.filter(state.mappingData.edges, function (data) {
      return data.source === evt.item._cfg.id
    })
    //判断是否为末尾节点，末尾节点不处理展开折叠
    if (arr.length === 0) {
      if (state.rootData.id === evt.item._cfg.model.id) {
        state.changeInitKey++
        setTimeout(() => {
          init(true, state.listDate.caseId)
        }, 100);

      } else {
        if (evt.item._cfg.model.leaveNumber == 2) {
          init(false, evt.item._cfg.model.id, true, evt)
        }
      }
    } else {
      return
      //收起节点
      delFilterChange(evt.item._cfg.id)
      state.mappingData.nodes.forEach((item, index) => {
        if (item.id == evt.item._cfg.id) {
          item.fx = item.fx2
          item.fy = item.fy2
        }
      })
      graph.changeData();
    }
  });
  graph.on('node:click', (evt) => {
    if (!props.isDetails) {
      return
    }
    clearTimeout(timer); //清除未执行的定时器
    timer = setTimeout(function () {
      if (!evt.item._cfg.model.isLock) {
        state.innerDialogInfo = evt.item._cfg.model
        state.dialogInnerVisible = true
      } else {
        ElMessage({ message: '节点已锁定', type: 'error' });
      }
    }, 250);

  });
  graph.on('canvas:click', clearAllStats);
}
const clearAllStats = () => {
  graph.setAutoPaint(false);
  graph.getNodes().forEach(function (node) {
    graph.clearItemStates(node);
  });
  graph.getEdges().forEach(function (edge) {
    graph.clearItemStates(edge);
  });
  graph.paint();
  graph.setAutoPaint(true);
  state.dialogInnerVisible = false
}

//删除数据节点以及线
const delFilterChange = (id) => {
  let targetArr = [];
  let arr = _.filter(state.mappingData.edges, function (data) {
    return data.source === id
  })
  arr.forEach(item => {
    targetArr.push(item.target)
  })
  targetArr.forEach(item => {
    delFilterChange(item)
  })
  state.mappingData.nodes = _.filter(state.mappingData.nodes, function (data) {
    return !targetArr.includes(data.id)
  })
  state.mappingData.edges = _.filter(state.mappingData.edges, function (data) {
    return data.source !== id
  })
}
//处理展开节点后，当前数据加亮
const highlightChange = (e) => {
  const item = e.item;
  graph.setAutoPaint(false);
  graph.getNodes().forEach(function (node) {
    graph.clearItemStates(node);
    graph.setItemState(node, 'dark', true);
  });
  graph.setItemState(item, 'dark', false);
  graph.setItemState(item, 'highlight', true);
  graph.getEdges().forEach(function (edge) {
    if (edge.getSource() === item) {
      graph.setItemState(edge.getTarget(), 'dark', false);
      graph.setItemState(edge.getTarget(), 'highlight', true);
      graph.setItemState(edge, 'highlight', true);
      edge.toFront();
    } else if (edge.getTarget() === item) {
      graph.setItemState(edge.getSource(), 'dark', false);
      graph.setItemState(edge.getSource(), 'highlight', true);
      graph.setItemState(edge, 'highlight', true);
      edge.toFront();
    } else {
      graph.setItemState(edge, 'highlight', false);
    }
  });
  graph.paint();
  graph.setAutoPaint(true);
}
const init = (isInit = false, key = '01000220221207000146', isChild = false, ev) => {
  let str = "match (n:`案件信息`{key:'" + key + "'}) CALL apoc.path.subgraphAll(n, {labelFilter:'-关键词' ,relationshipFilter:'>'}) YIELD nodes, relationships RETURN nodes,relationships"
  if (isChild) {
    str = "MATCH p= (n)-[r]->(m) where id(n)=" + key + " and  m.type<>'关键词' and  type(r)<>'案件当事人关系' RETURN p"
  } else {
    str = "MATCH p= (n:`案件信息`)-[r]-(m) where n.key='" + key + "' and  m.type<>'关键词' RETURN p"
  }
  let { d } = chatData
  if (isChild) {
    d = chatData2.d
  }
  if (!isChild) {
    state.mappingData.nodes = []
    state.mappingData.edges = []
  }
  state.typeArray = []
  let typeArr = []
  d.nodes.forEach((item) => {
    item.isLock = false
    if (item.type == undefined) {
      typeArr.push("无")
    } else {
      typeArr.push(item.type)
    }
  })
  let cData = Array.from(new Set(typeArr))
  cData.forEach((item, index) => {
    state.typeArray.push({
      label: item,
      color: colorArr[index]
    })
  })
  let degree = Math.floor(360 / (d.nodes.length - 1))
  if (isChild) {
    if (state.rootData.id == key) {
      d.nodes.forEach((item, index) => {
        if (index !== 0) {
          // let x1 = 600 + 200 * Math.cos((index * degree) * Math.PI / 180)
          // let y1 = 250 + 200 * Math.sin((index * degree) * Math.PI / 180)
          // item.fx = x1
          // item.fy = y1
          // item.fx2 = x1
          // item.fy2 = y1
          // item.degree = degree
          item.indexNumber = index
        } else {
          // item.fx = 600
          // item.fy = 250
          // item.fx2 = 600
          // item.fy2 = 250
          // item.degree = degree
          item.indexNumber = index
        }
      })
    } else {
      // if (d.nodes.length !== 0) {
      //   state.mappingData.nodes.forEach((item, index) => {
      //     if (item.id == key) {
      //       item.fx = 600 + 300 * Math.cos((item.indexNumber * item.degree) * Math.PI / 180)
      //       item.fy = 250 + 300 * Math.sin((item.indexNumber * item.degree) * Math.PI / 180)
      //     }
      //   })
      // }
    }
    state.mappingData.nodes = [...state.mappingData.nodes, ...d.nodes]
    state.mappingData.nodes.forEach(item => {
      item.id = String(item.id)
    })
    let a = []
    d.rels.forEach(item => {
      if (!["当事人信息维度当事人信息关联关系", "线索信息维度线索信息关联关系", "违法行为信息维度违法行为信息关联关系"].includes(item.type)) {
        a.push({
          source: String(item.start),
          target: String(item.end),
        })
      }
    })
    state.mappingData.edges = [...state.mappingData.edges, ...a]
    state.mappingData.nodes = deWeight(state.mappingData.nodes)
    graph.changeData();
    highlightChange(ev)
  } else {
    //是否定住节点位置
    d.nodes.forEach((item, index) => {
      item.leaveNumber = 2
      // if (index !== 0) {
      //   let x1 = 600 + 200 * Math.cos((index * degree) * Math.PI / 180)
      //   let y1 = 250 + 200 * Math.sin((index * degree) * Math.PI / 180)
      //   item.fx = x1
      //   item.fy = y1
      //   item.fx2 = x1
      //   item.fy2 = y1
      //   item.degree = degree
      //   item.indexNumber = index
      // } else {
      //   item.fx = 600
      //   item.fy = 250
      //   item.fx2 = 600
      //   item.fy2 = 250
      //   item.degree = degree
      //   item.indexNumber = index
      // }
    })
    state.mappingData.nodes = d.nodes;
    state.mappingData.nodes.forEach(item => {
      item.id = String(item.id)
    })
    d.rels.forEach(item => {
      state.mappingData.edges.push({
        source: String(item.start),
        target: String(item.end),
        label: item.type,

      })
    })
  }
  if (isInit) {
    let a = _.filter(state.mappingData.nodes, function (data) {
      return data.key == key
    })
    state.rootData = a[0] ? a[0] : []
    initChart()
  }

  function deWeight(arr) {
    for (var i = 0; i < arr.length - 1; i++) {
      for (var j = i + 1; j < arr.length; j++) {
        if (arr[i].id == arr[j].id) {
          arr.splice(j, 1);
          //因为数组长度减小1，所以直接 j++ 会漏掉一个元素，所以要 j--
          j--;
        }
      }
    }
    return arr;
  }
}
onMounted(() => {
  init(true, "322022000000329")
});
</script>

<style lang="scss" scoped>
.temp-page {
  width: 100%;
  height: 100%;

  #container {
    width: 100%;
    height: 100%;
  }
}

.inner-dialog {
  position: absolute;
  top: 50px;
  right: 50px;
  width: 300px;
  height: 550px;
  padding: 10px;
  background-color: #fbfcfc;
  border-radius: 10px;
  box-shadow: 0px 2px 4px rgba(10, 10, 20, 0.5);

  .inner-dialog-title {
    height: 30px;
    width: 100%;
    display: flex;
    align-items: center;
    justify-content: center;
    font-size: 18px;
  }

  .info-list {
    margin-bottom: 10px;

    .info-list-title {
      color: #000;
    }
  }
}

.slide-fade-enter-active {
  transition: all 0.3s ease-out;
}

.slide-fade-leave-active {
  transition: all 0.3s cubic-bezier(1, 0.5, 0.8, 1);
}

.slide-fade-enter-from,
.slide-fade-leave-to {
  transform: translateX(20px);
  opacity: 0;
}
</style>
